﻿using System;
using System.Collections.Generic;
using System.Linq;
using FoodBank.Core.Models;
using FoodBank.Core.Repository;
using FoodBank.Core.Services;
using FoodBank.Core.Validation;
using FoodBank.Service.Extensions;
using FoodBank.Service.Helpers;
using FoodBank.Service.Properties;

namespace FoodBank.Service
{
    public class ProductService : IProductService
    {
        private readonly IRepository<Product> _repository;
        private readonly IValidationDictionary _validationDictionary;

        /// <summary>
        /// Initializes a new instance of the <see cref="ProductService"/> class.
        /// </summary>
        /// <param name="repository">The repository.</param>
        /// <param name="validationDictionary">The validation dictionary.</param>
        public ProductService(IRepository<Product> repository, IValidationDictionary validationDictionary)
        {
            if (repository == null) 
                throw new ArgumentNullException("repository");
            if (validationDictionary == null)
                throw new ArgumentNullException("validationDictionary");

            _repository = repository;
            _validationDictionary = validationDictionary;
        }

        /// <summary>
        /// Gets the <see cref="Product"/> using the given id.
        /// </summary>
        /// <param name="productId">The product id.</param>
        /// <returns><see cref="Product"/></returns>
        public Product GetProduct(int productId)
        {
            if(productId == default(int))
                throw new ArgumentException(string.Format("Invalid product id. Value: {0}", productId));

            var product = _repository.RetrieveById(productId);

            if(product != null)
            {
                CalculatePercentages(product);
            }
            return product;
        }

        /// <summary>
        /// Persists the <see cref="Product"/> entity into the datastore.
        /// </summary>
        /// <param name="product">The product.</param>
        public void CreateProduct(Product product)
        {
            if (product == null)
                throw new ArgumentNullException("product");

           if(ValidateProduct(product))
               _repository.Add(product);
        }

        /// <summary>
        /// Persists a list of <see cref="Product"/> entities into the datastore.
        /// </summary>
        /// <param name="products">The products.</param>
        public void CreateProducts(IEnumerable<Product> products)
        {
            foreach (var product in products)
            {
                CreateProduct(product);
            }
        }

        /// <summary>
        /// Calculates the percentages.
        /// </summary>
        /// <param name="product">The product.</param>
        public static void CalculatePercentages(Product product)
        {
            if (product == null) 
                throw new ArgumentNullException("product");

            product.DailyRecommendedCaloriesPercentage = Calculator.Percentage(product.Calories, Settings.Default.DailyFoodIntake);
            product.DailyRecommendedSugarPercentage = Calculator.Percentage(product.Sugar.GetValueOrDefault(), Settings.Default.DailyRecommendedAmountSugar);
            product.DailyRecommendedFatPercentage = Calculator.Percentage(product.Fat, Settings.Default.DailyRecommendedAmountFat);
            product.DailyRecommendedSaturatedFatPercentage = Calculator.Percentage(product.SaturatedFat.GetValueOrDefault(), Settings.Default.DailyRecommendedAmountSaturatedFat);
            product.DailyRecommendedSaturatedFatPercentage = Calculator.Percentage(product.Salt.GetValueOrDefault(), Settings.Default.DailyRecommendedAmountSodium);
            product.DailyRecommendedProteinPercentage = Calculator.Percentage(product.Proteins, Settings.Default.DailyRecommendedAmountProtein);
            product.DailyRecommendedCarbohydratePercentage = Calculator.Percentage(product.Carbohydrates, Settings.Default.DailyRecommendedAmountCarbohydrate);
        }

        /// <summary>
        /// Checks if the <see cref="Product"/> entity already exists in the datastore.
        /// </summary>
        /// <param name="product">The product.</param>
        /// <returns>True if the <see cref="Product"/> entity already exists, otherwise false.</returns>
        public bool Exists(Product product)
        {
            return _repository.RetrieveMany(e => e.Name.ToLowerInvariant() == product.Name.ToLowerInvariant()).Any();
        }

        /// <summary>
        /// Validates the <see cref="Product"/> entity.
        /// </summary>
        /// <param name="product">The product.</param>
        /// <returns>True if the <see cref="Product"/> entity is valid, otherwise false.</returns>
        protected bool ValidateProduct(Product product)
        {
            if (Exists(product))
            {
                _validationDictionary.AddError("Name", "A product with the name: {0} already exists.".FormatInvariant(product.Name));
            }
            if (product.ProductCategory == null)
            {
                _validationDictionary.AddError("Brand", "You must specify a product category for this product");
            }
            if(product.Brand == null)
            {
                _validationDictionary.AddError("Brand", "You must specify a brand for this product");
            }
            return _validationDictionary.IsValid;
        }
    }
}